package com.dongnam.quanly.web;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dongnam.quanly.common.ObjectType;

public class DiffComparator {

	private static Logger logger = LoggerFactory.getLogger(DiffComparator.class);

	private final static String[] consoleUserMethods = { "Name", "Password", "Status", "GroupId" };
	private final static String[] userGroupMethods = { "GroupId", "GroupName"/*
																			 * ,
																			 * "UserRoles"
																			 */};
	private final static String[] userRoleMethods = { "RoleId", "RoleName"/*
																		 * ,
																		 * "UserRights"
																		 */};
	private final static String[] userRightMethods = { "TypeId", "AccessLevel" };
	private static Map<ObjectType, String[]> methods = new HashMap<ObjectType, String[]>();

	static {
		methods.put(ObjectType.User, consoleUserMethods);
		methods.put(ObjectType.UserGroup, userGroupMethods);
		methods.put(ObjectType.UserRole, userRoleMethods);
		methods.put(ObjectType.UserRight, userRightMethods);
	}

	public void setMethods(ObjectType className, String[] getterMethods) {
		methods.put(className, getterMethods);
	}

	// Give difference between two list
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public String[] compare(ObjectType className, List prev, List curr, Comparator comparator) {
		String[] diff = new String[2];
		String[] temp = new String[2];
		int iterations = prev.size();
		if (curr.size() > iterations)
			iterations = curr.size();
		Collections.sort(prev, comparator);
		Collections.sort(curr, comparator);
		for (int i = 0; i < iterations; i++) {
			logger.debug("ClassName: {}", prev.get(i).getClass().getCanonicalName());
			temp = compare(className, prev.get(i), curr.get(i));
			for (int j = 0; j < temp.length; j++) {
				diff[j] = diff[j] + temp[j];
				if (i < iterations - 1)
					diff[j] = diff[j] + ";";
			}
		}
		return diff;
	}

	/**
	 * Compare the origin object and updated object
	 * 
	 * @param className
	 * @param origin
	 * @param updated
	 * @return difference between origin and updated, store in String[],
	 *         String[0] is origin, String[1] is updated
	 */
	public String[] compare(ObjectType className, Object origin, Object updated) {
		String[] diff = new String[2];
		diff[0] = "";
		diff[1] = "";
		logger.debug("Class Name: {}", className);
		List<String> getterList = new ArrayList<String>();
		if (methods.containsKey(className)) {
			for (int i = 0; i < methods.get(className).length; i++) {
				getterList.add("get" + methods.get(className)[i]);
			}
		}
		String prev = "";
		String curr = "";
		for (int i = 0; i < getterList.size(); i++) {
			try {
				prev = String.valueOf(origin.getClass().getMethod(getterList.get(i), null).invoke(origin, null));
				curr = String.valueOf(updated.getClass().getMethod(getterList.get(i), null).invoke(updated, null));
				if (prev != null && !prev.equals(curr)) {
					diff[0] = diff[0] + methods.get(className)[i] + ": " + prev;
					diff[1] = diff[1] + methods.get(className)[i] + ": " + curr;
					if (i < methods.get(className).length - 1) {
						diff[0] = diff[0] + ";";
						diff[1] = diff[1] + ";";
					}
				}
			} catch (NoSuchMethodException e) {
				logger.error("Invalid method list. ");
			} catch (SecurityException e) {
				logger.error("Security violation when accessing objects, objects: {}, {}", origin, updated);
			} catch (IllegalAccessException e) {
				logger.error("Illegal access to getter methods in objects: {}, {}", origin, updated);
			} catch (IllegalArgumentException e) {
				logger.error("Getter methods is not null in objects: {}, {}", origin, updated);
			} catch (InvocationTargetException e) {
				logger.error("Could not invocate getter methods in objects: {}, {}", origin, updated);
			}
		}
		logger.debug("diff list: {}, {}", diff[0], diff[1]);
		return diff;
	}
}
